package com.xtoad.ecms.common.utils.generate;

import javax.persistence.Column;
import javax.persistence.Entity;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * 辅助工具-代码生成器
 *
 * @author xtoad
 * @date 2020/05/29
 */
public class Generator {
    /**
     * 实体类包路径
     */
    public static final String modelPackage = "com.xtoad.ecms.baseinfo.model";
    // 控制器包路径
    public static final String controllerPackage = modelPackage.replace(".model", ".controller");
    // 转换器包路径
    public static final String converterPackage = modelPackage.replace(".model", ".converter");
    // DTO包路径
    public static final String dtoPackage = modelPackage.replace(".model", ".dto");
    // repository包路径
    public static final String repositoryPackage = modelPackage.replace(".model", ".repository");
    // Service包路径
    public static final String servicePackage = modelPackage.replace(".model", ".service");

    /**
     * 是否强制覆盖
     */
    public static final boolean isOverwrite = false;

    /**
     * 日期
     */
    public static final String dateString = new SimpleDateFormat("yyyy/MM/dd").format(new Date());

    /**
     * 作者
     */
    public static final String author = "xtoad";

    public static final String sourceRoot = "D:\\zwt\\xtoad-ecms\\src\\main\\java\\";

    public static void main(String[] args) {
        String file = Thread.currentThread().getContextClassLoader().getResource("").getFile();
        System.out.println(file);
        // 服务器包路径
        final String servicePackage = modelPackage.replace(".model", ".service");
        Set<String> className = getClassName(modelPackage, false);
        System.out.println("read model ：");
        className.forEach(System.out::println);
        String name = "PageElement";
        // create repository
        String fileContent = "";
        String fileName = "";
        RepositoryGenerator repositoryGenerator = new RepositoryGenerator();
        fileContent = repositoryGenerator.generate(repositoryPackage, name);
        fileName = sourceRoot + repositoryPackage.replace(".", File.separator) + File.separator
                + "I" + name + "Repository.java";
        writeFile(fileName, fileContent);

        // create dto
        DtoGenerator dtoGenerator = new DtoGenerator();
        fileContent = dtoGenerator.generate(dtoPackage, fileName);
        fileName = sourceRoot + dtoPackage.replace(".", File.separator) + File.separator
                + name + "DTO.java";
        writeFile(fileContent, fileName);

        // create controller
        ControllerGenerator controllerGenerator = new ControllerGenerator();
        fileContent = controllerGenerator.generate(controllerPackage, name);
        fileName = sourceRoot + controllerPackage.replace(".", File.separator) + File.separator
                + name + "Controller.java";
        writeFile(fileName, fileContent);

        // create Service
        ServiceGenerator serviceGenerator = new ServiceGenerator();
        fileContent = serviceGenerator.generate(servicePackage, name);
        fileName = sourceRoot + servicePackage.replace(".", File.separator) + File.separator
                + "I" + name + "Service.java";
        writeFile(fileName, fileContent);

        // create ServiceImpl
        ServiceImpGenerator serviceImpGenerator = new ServiceImpGenerator();
        fileContent = serviceImpGenerator.generate(servicePackage, name);
        fileName = sourceRoot + servicePackage.replace(".", File.separator) + File.separator
                + "impl" + File.separator + name + "ServiceImpl.java";
        writeFile(fileName, fileContent);

        // create Converter
        ConverterGenerator converterGenerator = new ConverterGenerator();
        fileContent = converterGenerator.generate(converterPackage, name);
        fileName = sourceRoot + converterPackage.replace(".", File.separator) + File.separator
                + name + "Converter.java";
        writeFile(fileName, fileContent);
    }

    /**
     * 取得某个包下不带包路径的 类名
     *
     * @param packagePath 包路径
     * @param isRec       是否递归目录
     * @return
     */
    public static List<String> getClassNameNoPackage(String packagePath, boolean isRec) {
        Set<String> repositoryNames = getClassName(packagePath, isRec);
        repositoryNames.forEach(System.out::println);
        List<String> classNameList = new ArrayList<>();
        if (repositoryNames != null) {
            repositoryNames.forEach(x -> {
                String[] splits = x.split("\\.");
                classNameList.add(splits[splits.length - 1]);
            });
        }
        return classNameList;
    }

    /**
     * 获取某包下所有类
     *
     * @param packageName 包名
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    public static Set<String> getClassName(String packageName, boolean isRecursion) {
        Set<String> classNames = null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath = packageName.replace(".", "/");

        URL url = loader.getResource(packagePath);
        if (url != null) {
            System.out.println(url.getPath());
            String protocol = url.getProtocol();
            if (protocol.equals("file")) {
                classNames = getClassNameFromDir(url.getPath(), packageName, isRecursion);
            } else if (protocol.equals("jar")) {
                JarFile jarFile = null;
                try {
                    jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                } catch (Exception e) {
                    e.printStackTrace();
                }

                if (jarFile != null) {
                    getClassNameFromJar(jarFile.entries(), packageName, isRecursion);
                }
            }
        } else {
            /*从所有的jar包中查找包名*/
            classNames = getClassNameFromJars(((URLClassLoader) loader).getURLs(), packageName, isRecursion);
        }

        return classNames;
    }

    /**
     * 从项目文件获取某包下有类
     *
     * @param filePath    文件路径
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    public static Set<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) {
        Set<String> className = new HashSet<>();
        File file = new File(filePath);
        File[] files = file.listFiles();
        for (File childFile : files) {
            if (childFile.isDirectory()) {
                if (isRecursion) {
                    className.addAll(getClassNameFromDir(childFile.getPath(), packageName + "." + childFile.getName(), isRecursion));
                }
            } else {
                String fileName = childFile.getName();
                //endsWith() 方法用于测试字符串是否以指定的后�?结束�?  !fileName.contains("$") 文件名中不包? '$'
                if (fileName.endsWith(".class") && !fileName.contains("$")) {
                    className.add(packageName + "." + fileName.replace(".class", ""));
                }
            }
        }

        return className;
    }


    /**
     * @param jarEntries
     * @param packageName
     * @param isRecursion
     * @return
     */
    public static Set<String> getClassNameFromJar(Enumeration<JarEntry> jarEntries, String packageName,
                                                   boolean isRecursion) {
        Set<String> classNames = new HashSet();

        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            if (!jarEntry.isDirectory()) {
                /*
                 * 这里是为了方便，先把"/" 转成 "." 再判 ".class" 的做法可能会有bug
                 * (FIXME: 先把"/" 转成 "." 再判".class" 的做法可能会有bug)
                 */
                String entryName = jarEntry.getName().replace("/", ".");
                if (entryName.endsWith(".class") && !entryName.contains("$") && entryName.startsWith(packageName)) {
                    entryName = entryName.replace(".class", "");
                    if (isRecursion) {
                        classNames.add(entryName);
                    } else if (!entryName.replace(packageName + ".", "").contains(".")) {
                        classNames.add(entryName);
                    }
                }
            }
        }

        return classNames;
    }

    /**
     * 从所有jar中搜索该包，并获取该包下所有类
     *
     * @param urls        URL集合
     * @param packageName
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    public static Set<String> getClassNameFromJars(URL[] urls, String packageName, boolean isRecursion) {
        Set<String> classNames = new HashSet<>();

        for (int i = 0; i < urls.length; i++) {
            String classPath = urls[i].getPath();
            //不必搜索classes文件�?
            if (classPath.endsWith("classes/")) {
                continue;
            }

            JarFile jarFile = null;
            try {
                jarFile = new JarFile(classPath.substring(classPath.indexOf("/")));
            } catch (IOException e) {
                e.printStackTrace();
            }

            if (jarFile != null) {
                classNames.addAll(getClassNameFromJar(jarFile.entries(), packageName, isRecursion));
            }
        }

        return classNames;
    }

    public static Map<String, String> getClassComment(String className) {
        Map<String, String> commentMap = new HashMap<>(6);
        try {
            Class<?> aClass = Class.forName(className);
            String[] splits = aClass.getName().split("\\.");
            String classId = splits[splits.length - 1];
            String comment = classId;
            if (aClass.isAnnotationPresent(Entity.class)) {
                for (Annotation annotation : aClass.getAnnotations()) {
                    if (annotation != null && annotation instanceof Entity) {
                        comment = ((Entity) annotation).name();
                        break;
                    }
                }
            }
            commentMap.put(classId, comment);
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                String fieldComment = field.getName();
                if (field.isAnnotationPresent(Column.class)) {
                    for (Annotation annotation : field.getAnnotations()) {
                        if (annotation instanceof Column) {
                            fieldComment = ((Column) annotation).name();
                            break;
                        }
                    }
                }
                commentMap.put(field.getName(), fieldComment);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return commentMap;
    }

    public static Map<String, String> getClassFieldType(String className) {
        Map<String, String> commentMap = new HashMap<>(6);
        try {
            Class<?> aClass = Class.forName(className);
            Field[] fields = aClass.getDeclaredFields();
            for (Field field : fields) {
                String[] strings = field.getType().getName().split("\\.");
                commentMap.put(field.getName(), strings[strings.length - 1]);
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return commentMap;
    }

    public static void writeFiles(Map<String, String> files) {
        files.keySet().forEach(file -> {
            String fileName = sourceRoot + file.replace(".", File.separator) + ".java";
            writeFile(fileName, files.get(file));
        });
    }

    public static void writeFile(String fileName, String content) {
        System.out.println("generate file " + fileName);
        BufferedWriter writer = null;
        try {
            String dir = fileName.substring(0, fileName.lastIndexOf(File.separator));
            File df = new File(dir);
            if (!df.exists()) {
                df.mkdirs();
            }
            File f = new File(fileName);
            if (f.exists()) {
                f.delete();
            }
            f.createNewFile();
            writer = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(f), "utf-8"));
            writer.write(content);
            writer.close();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
        }
    }

    public static String firstCharUpper(String str) {
        char[] cs = str.toCharArray();
        if (Character.isUpperCase(cs[0])) {
            return String.valueOf(cs);
        }
        cs[0] -= 32;
        return String.valueOf(cs);
    }

    public static String firstCharLower(String str) {
        char[] cs = str.toCharArray();
        if (Character.isLowerCase(cs[0])) {
            return String.valueOf(cs);
        }
        cs[0] += 32;
        return String.valueOf(cs);
    }
}
